﻿
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Options;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Net;
    using System.Security.Claims;
    using System.Threading;
    using System.Threading.Tasks;

namespace NetDiskStorageWebApi.Configuration
{
    /// <summary>
    /// API 请求频率限制过滤器
    /// </summary>
    public class ApiRateLimitAttribute : ActionFilterAttribute
        {
            // 配置选项
            public int SecLimit { get; set; }
            public int MinLimit { get; set; }

            /// <summary>
            /// 按秒限制
            /// </summary>
            public ApiRateLimitAttribute(int secLimit)
            {
                SecLimit = secLimit;
                MinLimit = 0;
            }

            /// <summary>
            /// 按秒和分钟限制
            /// </summary>
            public ApiRateLimitAttribute(int secLimit, int minLimit)
            {
                SecLimit = secLimit;
                MinLimit = minLimit;
            }

            public override async Task OnActionExecutionAsync(
                ActionExecutingContext context,
                ActionExecutionDelegate next)
            {
                // 1. 获取所需服务
                var configService = context.HttpContext.RequestServices
                    .GetService<IApiRateLimitConfigService>();
                var store = context.HttpContext.RequestServices
                    .GetService<IApiRateLimitStore>();
                var options = context.HttpContext.RequestServices
                    .GetService<IOptions<RateLimitOptions>>();
                var timeProvider = context.HttpContext.RequestServices
                    .GetService<ITimeProvider>() ?? new DefaultTimeProvider();

                // 2. 检查是否启用
                if (options?.Value.Enabled == false)
                {
                    await next();
                    return;
                }

                // 3. 认证检查
                var user = context.HttpContext.User;
                if (!user.Identity.IsAuthenticated)
                {
                    await next();
                    return;
                }

                // 4. 豁免检查
                if (user.FindFirstValue("NoApiLimit") == "1")
                {
                    await next();
                    return;
                }

            // 5. 获取用户和路径
            // 替换这里的 "user_id" 为您的JWT中实际的声明名称
            //string userId = user.FindFirstValue("userId");              // 标准声明name
              var userId = user?.FindFirstValue("userId"); // 直接从 "userId" 声明获取
              var path = context.HttpContext.Request.Path;

                // 6. 获取配置限制（优先使用特性参数）
                var limits = (GetEffectiveLimit(SecLimit), GetEffectiveLimit(MinLimit));
                if (configService != null)
                {
                    var customLimits = configService.GetLimits(userId, path);
                    limits = (
                        customLimits.SecLimit > 0 ? customLimits.SecLimit : GetEffectiveLimit(SecLimit),
                        customLimits.MinLimit > 0 ? customLimits.MinLimit : GetEffectiveLimit(MinLimit)
                    );
                }

                // 7. 检查限制
                if (CheckRateLimit(store, timeProvider, userId, path, limits))
                {
                    await next();
                    return;
                }

                // 8. 超出限制的处理
                context.Result = new ObjectResult(new
                {
                    code = (int)HttpStatusCode.TooManyRequests,
                    message = options?.Value.ErrorMessage ?? "请求过于频繁，请稍后再试"
                })
                {
                    StatusCode = (int)HttpStatusCode.TooManyRequests
                };
            }

            private bool CheckRateLimit( IApiRateLimitStore store,ITimeProvider time,string userId,string path,(int SecLimit, int MinLimit) limits)
            {
                var now = time.UtcNow;

                // 秒级检查
                if (limits.SecLimit > 0)
                {
                    var secKey = $"sec_{userId}_{path}_{now:yyyyMMddHHmmss}";
                    var secCount = store.IncrementAndGet(secKey);
                    if (secCount > limits.SecLimit) return false;
                }

                // 分钟级检查
                if (limits.MinLimit > 0)
                {
                    var minKey = $"min_{userId}_{path}_{now:yyyyMMddHHmm}";
                    var minCount = store.IncrementAndGet(minKey);
                    if (minCount > limits.MinLimit) return false;
                }

                return true;
            }

            private int GetEffectiveLimit(int limit) =>
                limit < 0 ? int.MaxValue : limit; // 负值表示无限制
        }

        // 支持接口和实现
        public interface IApiRateLimitConfigService
        {
            (int SecLimit, int MinLimit) GetLimits(string userId, string path);
        }

        public interface IApiRateLimitStore
        {
            int IncrementAndGet(string key);
        }

        public class MemoryRateLimitStore : IApiRateLimitStore
        {
        //ConcurrentDictionary<string, int> 是 C# 中用于线程安全操作的键值对集合
        //原子操作方法：
        //提供 GetOrAdd、AddOrUpdate、TryAdd、TryUpdate、TryRemove 等原子操作，避免多线程竞争导致的数据不一致问题。
        private readonly ConcurrentDictionary<string, int> _counters;

        public MemoryRateLimitStore()
        {
            // 显式初始化 ConcurrentDictionary
            _counters = new ConcurrentDictionary<string, int>();
        }
        public int IncrementAndGet(string key) =>
                _counters.AddOrUpdate(key, 1, (_, val) => val + 1);
        // 添加GetKeys方法以获取所有键的集合
        public IEnumerable<string> GetKeys() => _counters.Keys;

        // 添加RemoveKeys方法用于移除过期键
        public void RemoveKeys(IEnumerable<string> keys)
        {
            foreach (var key in keys)
            {
                _counters.TryRemove(key, out _);
            }
        }

    }

        public class RateLimitOptions
        {
            public bool Enabled { get; set; } = true;
            public string ErrorMessage { get; set; } = "请求过于频繁，请稍后再试";
            public int CleanupIntervalSeconds { get; set; } = 60;
        }

        public interface ITimeProvider
        {
            DateTime UtcNow { get; }
        }

        public class DefaultTimeProvider : ITimeProvider
        {
            public DateTime UtcNow => DateTime.UtcNow;
        }

        // 后台清理服务（可选）
        public class RateLimitCleanupService : BackgroundService
        {
            private readonly IApiRateLimitStore _store;
        // Fix for CS0246: Replace "TimeProvider" with "ITimeProvider" in the field declaration
        private readonly ITimeProvider _time;
            private readonly TimeSpan _cleanupInterval;
            private readonly TimeSpan _secExpiry = TimeSpan.FromSeconds(60);
            private readonly TimeSpan _minExpiry = TimeSpan.FromMinutes(60);

            public RateLimitCleanupService(
                IApiRateLimitStore store,
                IOptions<RateLimitOptions> options,
                ITimeProvider timeProvider)
            {
                _store = store;
                _time = timeProvider;
                _cleanupInterval = TimeSpan.FromSeconds(options.Value.CleanupIntervalSeconds);
            }

            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    if (_store is MemoryRateLimitStore memoryStore)
                    {
                        CleanMemoryStore(memoryStore);
                    }
                    await Task.Delay(_cleanupInterval, stoppingToken);
                }
            }

            private void CleanMemoryStore(MemoryRateLimitStore store)
            {
                var now = _time.UtcNow;
                var keysToRemove = new List<string>();

                foreach (var key in store.GetKeys())
                {
                    var parts = key.Split('_');
                    if (parts.Length < 4) continue;

                    var type = parts[0];
                    var timestamp = parts[3];

                    if ((type == "sec" && ShouldExpire(timestamp, now, "yyyyMMddHHmmss", _secExpiry)) ||
                        (type == "min" && ShouldExpire(timestamp, now, "yyyyMMddHHmm", _minExpiry)))
                    {
                        keysToRemove.Add(key);
                    }
                }

                store.RemoveKeys(keysToRemove);
            }

            private bool ShouldExpire(string timestampStr, DateTime now, string format, TimeSpan maxAge)
            {
                if (DateTime.TryParseExact(timestampStr, format, null, System.Globalization.DateTimeStyles.None, out var timestamp))
                {
                    return (now - timestamp) > maxAge;
                }
                return true;
            }
        }
    }

